<?php
/**
 * Created by PhpStorm.
 * User: qch
 * Date: 2015/5/26
 * Time: 17:36
 */

namespace Aoe\Core;

use Aoe\Attributes\MethodInterface;
use Aoe\Intent\Request\Request;
use Aoe\Util\Logger\NameRecorder;
use Aoe\Util\Pipeline;
use Closure;
use Exception;
use ReflectionException;
use ReflectionMethod;
use Throwable;

/**
 * # 控制器类
 *   * 为了保持在中间件中的一致性， 【请求】作为参数传递
 *
 */
class Controller
{
    const string ERR_ACT_NOT_FOUND   = '未找到${action}动作';
    const string MIDDLEWARE_START    = '开始执行中间件';
    const string MIDDLEWARE_COMPLETE = '中间件执行完成';
    const string ACTION_START        = '开始执行主方法';
    const string ACTION_COMPLETE     = '主方法执行完成';
    const string ERR_ATTR            = '向${method}添加${attr}失败';

    /**
     * @var string 控制器ID
     */
    public string $name {
        get {
            return $this->name;
        }
    }
    /**
     * @var Kernel 内核
     */
    public Kernel $kernel {
        get {
            return $this->kernel;
        }
    }

    /**
     * 中间件列表
     *
     * @var Closure
     */
    private array  $middlewares = [];
    /**
     * @var NameRecorder|null 日志记录工具
     */
    public ?NameRecorder $recorder    = null {
        get {
            if (!$this->recorder) $this->recorder = $this->module->recorder;
            return $this->recorder;
        }
    }

    public function __construct(protected(set) Module $module {
        get {
            return $this->module;
        }
    }, string                            $name)
    {
        $this->kernel = $module->kernel;
        $this->name = ucfirst($name);
    }

    /**
     * ## 获取 $action 的全部配置
     * @param string|null $action
     * @param mixed $default
     * @return mixed
     */
    public function getConfigOf(?string $action = null, mixed $default = null): mixed
    {
        return $this->module->getConfigOf(strtolower($this->name), $action, $default);
    }

    /**
     * ## 新增中间件
     *
     * @param Closure(Closure(Request $request):void):void $middleware
     */
    public function setMiddleware(Closure $middleware): void
    {
        $this->middlewares[] = $middleware;
    }

    /**
     * ## 执行
     * @throws Exception
     */
    public function invoke(Request $request): void
    {
        $this->on_invoke($request);
        $this->after_invoke();
    }

    /**
     * ## 一些收尾的工作，无条件执行
     *
     * @return void
     */
    protected function after_invoke(){}

    /**
     * ## 执行控制器中定义的方法
     *
     * @throws Exception
     */
    private function on_invoke(Request $request): void
    {
        $closure = $this->_make_core_middleware($request);
        $this->recorder->Log(self::MIDDLEWARE_START);
        Pipeline::start($this->middlewares, $closure, $request);
        $this->recorder->Log(self::MIDDLEWARE_COMPLETE);
    }

    /**
     * ## 设置内核方法
     *
     * @param Request $request
     * @return Closure(Request $request): void
     * @throws Exception
     */
    private function _make_core_middleware(Request $request): Closure
    {
        $closure = $this->_get_core_method($request);
        return $closure ? function (Request $request) use ($closure) {
            $this->recorder->Log(self::ACTION_START);
            $closure && $closure($request);
            $this->recorder->Log(self::ACTION_COMPLETE);
        } : $this->_miss_method(...);
    }
    
    /**
     * 添加核心中间件--自动执行方法
     *
     * @param Request $request
     *
     * @return null | (Closure(Request $request): void)
     * @throws Exception
     */
    private function _get_core_method(Request $request): ?Closure
    {
        $method = $this->get_core_method_name($request);
        if ($method && !method_exists($this, $method)) {
            $this->recorder->Log(self::ERR_ACT_NOT_FOUND, ['action' => $method]);
            return null;
        }
        return $this->method_attributes($method);
    }

    /**
     * 添加方法注解中间件
     *
     * @param string $method 方法名
     *
     * @return Closure(Request $request): void
     * @throws ReflectionException
     */
    private function method_attributes(string $method): Closure
    {
        $core = $this->$method(...);

        // 处理方法注解
        $rm = new ReflectionMethod($this, $method);
        foreach ($rm->getAttributes() as $attribute) {
            try {
                /** @var MethodInterface $instance */
                $instance = $attribute->newInstance();
                $closure  = $instance->forMethod($rm, $this);

                $core = $closure($core);
            } catch (Throwable) {
                $this->recorder->Error(
                    self::ERR_ATTR,
                    ['method' => $method, 'attr' => $attribute->getName()]
                );
            }
        }

        return $core;
    }
    
    protected function get_core_method_name(Request $request): string | false
    {
        return strtolower($request->section->action) . '_action';
    }

    /**
     * ## 未命中，则执行
     * @throws Exception
     */
    private function _miss_method(Request $request): void {}
}